package membership

import (
	"context"
	"fmt"
	"log"
	"sync"
	"time"
)

// ClusterEvent 表示集群事件类型
type ClusterEvent int

const (
	// ClusterEventMemberJoin 表示成员加入事件
	ClusterEventMemberJoin ClusterEvent = iota
	// ClusterEventMemberLeave 表示成员离开事件
	ClusterEventMemberLeave
	// ClusterEventMemberFailed 表示成员失败事件
	ClusterEventMemberFailed
	// ClusterEventMemberUpdate 表示成员更新事件
	ClusterEventMemberUpdate
)

// String 返回集群事件的字符串表示
func (e ClusterEvent) String() string {
	switch e {
	case ClusterEventMemberJoin:
		return "member-join"
	case ClusterEventMemberLeave:
		return "member-leave"
	case ClusterEventMemberFailed:
		return "member-failed"
	case ClusterEventMemberUpdate:
		return "member-update"
	default:
		return "unknown"
	}
}

// ClusterEventDelegate 表示集群事件委托
type ClusterEventDelegate interface {
	// OnMemberJoin 处理成员加入事件
	OnMemberJoin(member *Member)
	// OnMemberLeave 处理成员离开事件
	OnMemberLeave(member *Member)
	// OnMemberFailed 处理成员失败事件
	OnMemberFailed(member *Member)
	// OnMemberUpdate 处理成员更新事件
	OnMemberUpdate(member *Member)
}

// ClusterConfig 表示集群配置
type ClusterConfig struct {
	// NodeID 是节点ID
	NodeID string
	// NodeName 是节点名称
	NodeName string
	// BindAddr 是绑定地址
	BindAddr string
	// AdvertiseAddr 是广播地址
	AdvertiseAddr string
	// Tags 是节点标签
	Tags map[string]string
	// SuspectTimeout 是可疑超时时间
	SuspectTimeout time.Duration
	// DeadTimeout 是死亡超时时间
	DeadTimeout time.Duration
	// ProbeInterval 是探测间隔时间
	ProbeInterval time.Duration
	// ProbeTimeout 是探测超时时间
	ProbeTimeout time.Duration
	// IndirectChecks 是间接检查次数
	IndirectChecks int
	// GossipInterval 是八卦间隔时间
	GossipInterval time.Duration
	// GossipFanout 是八卦扇出数量
	GossipFanout int
}

// DefaultClusterConfig 返回默认集群配置
func DefaultClusterConfig() *ClusterConfig {
	return &ClusterConfig{
		SuspectTimeout:  5 * time.Second,
		DeadTimeout:     30 * time.Second,
		ProbeInterval:   1 * time.Second,
		ProbeTimeout:    500 * time.Millisecond,
		IndirectChecks:  3,
		GossipInterval:  200 * time.Millisecond,
		GossipFanout:    3,
		Tags:            make(map[string]string),
	}
}

// ClusterManager 表示集群管理器
type ClusterManager struct {
	// config 是集群配置
	config *ClusterConfig
	// memberList 是成员列表
	memberList *MemberList
	// localMember 是本地成员
	localMember *Member
	// delegates 是事件委托列表
	delegates []ClusterEventDelegate
	// delegatesMu 是委托列表的互斥锁
	delegatesMu sync.RWMutex
	// ctx 是上下文
	ctx context.Context
	// cancel 是取消函数
	cancel context.CancelFunc
	// wg 是等待组
	wg sync.WaitGroup
}

// NewClusterManager 创建一个新的集群管理器
func NewClusterManager(config *ClusterConfig) (*ClusterManager, error) {
	if config == nil {
		config = DefaultClusterConfig()
	}

	// 验证配置
	if config.NodeID == "" {
		return nil, fmt.Errorf("node ID is required")
	}
	if config.BindAddr == "" {
		return nil, fmt.Errorf("bind address is required")
	}

	// 如果未指定广播地址，则使用绑定地址
	if config.AdvertiseAddr == "" {
		config.AdvertiseAddr = config.BindAddr
	}

	// 如果未指定节点名称，则使用节点ID
	if config.NodeName == "" {
		config.NodeName = config.NodeID
	}

	ctx, cancel := context.WithCancel(context.Background())

	// 创建本地成员
	localMember := NewMember(config.NodeID, config.NodeName, config.AdvertiseAddr, config.Tags)

	// 创建成员列表
	memberList := NewMemberList()
	memberList.Add(localMember)

	return &ClusterManager{
		config:     config,
		memberList: memberList,
		localMember: localMember,
		delegates:  make([]ClusterEventDelegate, 0),
		ctx:        ctx,
		cancel:     cancel,
	}, nil
}

// Start 启动集群管理器
func (cm *ClusterManager) Start() error {
	log.Printf("Starting cluster manager for node %s (%s)", cm.config.NodeName, cm.config.NodeID)

	// 启动故障检测
	cm.wg.Add(1)
	go cm.failureDetectionLoop()

	// 启动八卦传播
	cm.wg.Add(1)
	go cm.gossipLoop()

	return nil
}

// Stop 停止集群管理器
func (cm *ClusterManager) Stop() error {
	log.Printf("Stopping cluster manager for node %s (%s)", cm.config.NodeName, cm.config.NodeID)

	// 取消上下文
	cm.cancel()

	// 等待所有goroutine退出
	cm.wg.Wait()

	return nil
}

// Join 加入集群
func (cm *ClusterManager) Join(addresses []string) (int, error) {
	// 这里应该实现实际的加入逻辑，例如联系种子节点
	// 简化起见，我们只是记录日志
	log.Printf("Node %s (%s) joining cluster via %v", cm.config.NodeName, cm.config.NodeID, addresses)
	return len(addresses), nil
}

// Leave 离开集群
func (cm *ClusterManager) Leave() error {
	// 更新本地成员状态为离开
	cm.localMember.UpdateStatus(MemberStatusLeft, cm.localMember.Incarnation+1)

	// 通知其他节点
	// 这里应该实现实际的通知逻辑
	log.Printf("Node %s (%s) leaving cluster", cm.config.NodeName, cm.config.NodeID)

	// 触发事件
	cm.triggerMemberEvent(ClusterEventMemberLeave, cm.localMember)

	return nil
}

// Members 获取所有成员
func (cm *ClusterManager) Members() []*Member {
	return cm.memberList.GetAll()
}

// Member 获取指定成员
func (cm *ClusterManager) Member(id string) (*Member, bool) {
	return cm.memberList.Get(id)
}

// LocalMember 获取本地成员
func (cm *ClusterManager) LocalMember() *Member {
	return cm.localMember
}

// AddDelegate 添加事件委托
func (cm *ClusterManager) AddDelegate(delegate ClusterEventDelegate) {
	cm.delegatesMu.Lock()
	defer cm.delegatesMu.Unlock()
	cm.delegates = append(cm.delegates, delegate)
}

// RemoveDelegate 移除事件委托
func (cm *ClusterManager) RemoveDelegate(delegate ClusterEventDelegate) bool {
	cm.delegatesMu.Lock()
	defer cm.delegatesMu.Unlock()

	for i, d := range cm.delegates {
		if d == delegate {
			cm.delegates = append(cm.delegates[:i], cm.delegates[i+1:]...)
			return true
		}
	}
	return false
}

// UpdateTags 更新本地成员标签
func (cm *ClusterManager) UpdateTags(tags map[string]string) {
	// 更新本地成员标签
	cm.localMember.Tags = tags
	cm.localMember.Incarnation++

	// 触发事件
	cm.triggerMemberEvent(ClusterEventMemberUpdate, cm.localMember)
}

// failureDetectionLoop 故障检测循环
func (cm *ClusterManager) failureDetectionLoop() {
	defer cm.wg.Done()

	ticker := time.NewTicker(cm.config.ProbeInterval)
	defer ticker.Stop()

	for {
		select {
		case <-cm.ctx.Done():
			return
		case <-ticker.C:
			cm.checkMembers()
		}
	}
}

// gossipLoop 八卦传播循环
func (cm *ClusterManager) gossipLoop() {
	defer cm.wg.Done()

	ticker := time.NewTicker(cm.config.GossipInterval)
	defer ticker.Stop()

	for {
		select {
		case <-cm.ctx.Done():
			return
		case <-ticker.C:
			cm.gossip()
		}
	}
}

// checkMembers 检查成员状态
func (cm *ClusterManager) checkMembers() {
	// 获取所有存活成员
	aliveMembers := cm.memberList.GetAlive()

	// 检查每个成员
	for _, member := range aliveMembers {
		// 跳过本地成员
		if member.ID == cm.localMember.ID {
			continue
		}

		// 检查心跳时间
		if time.Since(member.LastHeartbeat) > cm.config.SuspectTimeout {
			// 标记为可疑
			if member.UpdateStatus(MemberStatusSuspected, member.Incarnation) {
				log.Printf("Member %s (%s) is now suspected", member.Name, member.ID)
				cm.triggerMemberEvent(ClusterEventMemberUpdate, member)
			}
		}

		// 检查可疑时间
		if member.Status == MemberStatusSuspected && time.Since(member.LastStatusChange) > cm.config.DeadTimeout {
			// 标记为死亡
			if member.UpdateStatus(MemberStatusDead, member.Incarnation) {
				log.Printf("Member %s (%s) is now dead", member.Name, member.ID)
				cm.triggerMemberEvent(ClusterEventMemberFailed, member)
			}
		}
	}
}

// gossip 八卦传播
func (cm *ClusterManager) gossip() {
	// 获取所有存活成员
	aliveMembers := cm.memberList.GetAlive()
	if len(aliveMembers) <= 1 {
		return
	}

	// 选择要传播的成员
	// 这里应该实现实际的选择逻辑，例如随机选择
	// 简化起见，我们只是记录日志
	log.Printf("Node %s (%s) gossiping to %d members", cm.config.NodeName, cm.config.NodeID, len(aliveMembers)-1)
}

// triggerMemberEvent 触发成员事件
func (cm *ClusterManager) triggerMemberEvent(event ClusterEvent, member *Member) {
	cm.delegatesMu.RLock()
	defer cm.delegatesMu.RUnlock()

	for _, delegate := range cm.delegates {
		switch event {
		case ClusterEventMemberJoin:
			delegate.OnMemberJoin(member)
		case ClusterEventMemberLeave:
			delegate.OnMemberLeave(member)
		case ClusterEventMemberFailed:
			delegate.OnMemberFailed(member)
		case ClusterEventMemberUpdate:
			delegate.OnMemberUpdate(member)
		}
	}
}
